/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cipher;


import java.io.IOException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class MySignature {

    public MySignature() {
    }

    /**
     * Create a pair of keys for RSA.
     * @return HashMap<String,String> - a pair of key encoded with sun.misc.BASE64Encoder :
     *      - ["public"] -> public key
     *      - ["private"] -> private key
     * @see sun.misc.BASE64Encoder
     */
    public HashMap<String,String> generateKeys() throws NoSuchAlgorithmException, NoSuchProviderException{
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG","SUN");

        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        int keysize=1024;
        keyGen.initialize(keysize, random);

        KeyPair pair = keyGen.generateKeyPair();
        PrivateKey priv = pair.getPrivate();
        PublicKey pub = pair.getPublic();
        HashMap<String,String> keys = new HashMap<String, String>();
        keys.put("public", new BASE64Encoder().encode(pub.getEncoded()).toString());
        keys.put("private", new BASE64Encoder().encode(priv.getEncoded()).toString());
                
        return keys;
    }
    
    /**
     * Generate a signature for the given message with given private key.
     * @param privateKey    String - a private key, created with RSA and encoded with sun.misc.BASE64Encoder.
     * @param message       String - a message to sign.
     * @return      String - Signed message encoded with sun.misc.BASE64Encoder.
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws InvalidKeyException
     * @throws IOException
     * @throws SignatureException
     * @throws InvalidKeySpecException
     * @see sun.misc.BASE64Encoder
     */
    public String generateSignature(String privateKey, String message)
            throws NoSuchAlgorithmException,
            NoSuchProviderException,
            InvalidKeyException,
            IOException, SignatureException, InvalidKeySpecException {

        Signature signature = Signature.getInstance("SHA1withRSA");
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG","SUN");
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");

        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(privateKey));
        PrivateKey priv = keyFactory.generatePrivate(privateKeySpec);
        signature.initSign(priv,random);
        signature.update(message.getBytes());
        byte[] sign = signature.sign();
        return new BASE64Encoder().encode(sign);

    }
    
    /**
     * Encode the given message with given private key using RSA.
     * @param privateKey    String - a private key, created with RSA and encoded with sun.misc.BASE64Encoder.
     * @param message       String - a message to encode.
     * @return      String - Encoded message encoded with RSA and sun.misc.BASE64Encoder.
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeySpecException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws NoSuchProviderException
     * @throws IOException 
     * @see sun.misc.BASE64Encoder
     */
    public String encodeMessage(String privateKey, String message) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, IOException{
        Cipher cipher = Cipher.getInstance("RSA");
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG","SUN");
        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(privateKey));
        PrivateKey priv = keyFactory.generatePrivate(privateKeySpec);
        cipher.init(Cipher.ENCRYPT_MODE, priv,random);	
        byte[] encoded = cipher.doFinal(message.getBytes());
        return new BASE64Encoder().encode(encoded);
    }
    
    /**
     * Decode the given message with given public key using RSA.
     * @param publicKey    String - a public key, created with RSA and encoded with sun.misc.BASE64Encoder.
     * @param encoded       String - a message to decode.
     * @return      String - Decoded message encoded with sun.misc.BASE64Encoder.
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeySpecException
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws NoSuchProviderException
     * @throws IOException 
     * @see sun.misc.BASE64Encoder
     */
    public String decodeMessage(String publicKey, String encoded) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, IOException{

        Cipher cipher = Cipher.getInstance("RSA");
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG","SUN");
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(new BASE64Decoder().decodeBuffer(publicKey));
        PublicKey pub = keyFactory.generatePublic(publicKeySpec);
        
        cipher.init(Cipher.DECRYPT_MODE, pub, cipher.getParameters(),random);

        byte[] decoded = cipher.doFinal(new BASE64Decoder().decodeBuffer(encoded));
        System.out.println("Decoded : "+new String(decoded));
        return new String(decoded);
    }
    
    /**
     * Verify the origin of the message using signature and encoded message.
     * @param publicKey String - a public key, created with RSA and encoded with sun.misc.BASE64Encoder.
     * @param sign      String - a signature created with generateSignature.
     * @param message   String - an encoded message.
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeySpecException
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws NoSuchProviderException
     * @throws IOException
     * @throws SignatureException 
     * @see sun.misc.BASE64Encoder
     * @see MySignature#generateSignature(java.lang.String, java.lang.String) 
     */
    public boolean verifyMessageSign(String publicKey, String sign, String message) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, IOException, SignatureException{

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(new BASE64Decoder().decodeBuffer(publicKey));
        PublicKey pub = keyFactory.generatePublic(publicKeySpec);
        
        Signature sig = Signature.getInstance("SHA1withRSA");
        sig.initVerify(pub);
        sig.update(message.getBytes());
        return sig.verify(new BASE64Decoder().decodeBuffer(sign));
    }
}
